package cn.blank.calc;

/**
 * 数制的转换和存储
 *
 * @author _blank    DateTime 2022/7/15 15:06:28
 */
public class ConversionAndStorageOfNumberSystems {

    /*
     * 数制
     *     数制也称计数制，是指用一组固定的符号和统一的规则来表示数值的方法。
     *     计算机处理的信息必须转换成二进制形式数据后才能进行存储和传输。
     *     计算机中，经常使用的进制有二进制、八进制、十进制、十六进制。
     *     1、二进制数
     *         二进制数由两个基本数字0、1组成，二进制数的运算规律是逢二进一。
     *         二进制数的书写通常在数的右下方注上基数2，或在后面加B与其他进制加以区别，如二进制100101可以写成(100101)₂或写成100101B。
     *     2、八进制数
     *         八进制是由0~7共8个数字组成的，运算规则是逢八进一。
     *         八进制的基R=8=2³，并且每个数码正好对应三位二进制数，所以八进制能很好地反映二进制。
     *         八进制数据表示时用下标8或数据后面加O表示，如八进制261写成(261)₈、(261)O。
     *     3、十进制数
     *         十进制数使我们常用的数据表示方法，由0~9共10个数字组成，运算规则是逢十进一。
     *         表示时用下标10或者数据后面加D，也可以省略。
     *     4、十六进制数
     *         十六进制数由0~9及A~F共16个数字组成，A~F分别表示十进制数10~15，运算规则是逢十六进一。
     *         通常在表示时用下标16或者数据后面加H，如(1FA)₁₆或(1FA)H。
     *     Tips  在C语言程序中，我们可以通常按十进制写，如果写的是十六进制，需要以0x开头；八进制以0开头，如0123表示八进制的123,0x123则表示十六进制的123.
     * 数制的转换
     *     我们知道计算机中数据是以二进制的形式存在的，但是二进制数据太长，没有人原意对很长的二进制进行操作，用十六进制或者八进制可以解决这个问题，因为进制越大，数的表达长度也就越短。
     *     不过，为什么偏偏是十六进制或八进制，而不是其他的诸如九或者二十进制呢？
     *     因为2、8、16，分别是2的1次方、3次方、4次方，这一点使得三种进制直接可以非常直接地相互转换。
     *     八进制或十六进制既缩短了二进制数又保持了二进制数的表达特点。
     *     1、二进制、八进制、十六进制转换成十进制
     *         规则：数码乘以各自的权的累加
     *     2、十进制转换成二进制、八进制、十六进制
     *         规则如下：
     *         > 整数部分：除以进制取余数，直到商为0，余数从下到上排列。
     *         > 小数部分：乘以进制取整数，得到的整数从上到下排列。
     *     3、二进制转换成八进制
     *         规则如下：
     *         > 整数部分：从右向左按三位进行分组，不足补零。
     *         > 小数部分：从左向右按三位进行分组，不足补零。
     *     4、二进制转换成十六进制
     *         规则如下：
     *         > 整数部分：从右向左按四位进行分组，不足补零。
     *         > 小数部分：从左向右按四位进行分组，不足补零。
     *     5、八进制、十六进制转换成二进制
     *         规则如下：
     *         > 一位八进制对应三位二进制
     *         > 一位十六进制对应四位二进制
     * 计算机中数据的存储
     *     我们已经知道在计算机内所有的数据最终都是使用二进制数表示的，上一节中我们已经学习了如何将一个十进制、八进制、十六进制数转换为二进制数。
     *     数值有正负之分，它们在计算机内是如何存储的呢？
     *     在计算机中，数据有三种表示方法：原码、反码和补码。
     *     计算机用一个二进制的最高位存放所表示数值的符号，最高位为0表示正数，最高位为1表示负数。
     *     对于一个正数，原码是将该数转换成二进制，它的反码和补码与原码相同。
     *     对于一个负数，原码是将该数按照绝对值大小转换成的二进制数，最高位即符号位为1；
     *                 它的反码是除符号位外将二进制数按位取反，所得的新二进制数称为原二进制数的反码；
     *                 它的补码是将其二进制的反码加1。
     *     计算机中任何一个带有符号的二进制数都是以补码形式进行运算和存储的。
     *     如表1-1所示为比较1与-1的原码、反码和补码。
     *     表1-1 数据存储表（1与-1的原码、反码和补码）
     *     |    整数    |    原码    |    反码    |    补码    |
     *     |-----------|------------|------------|-----------|
     *     |     1     |  0000 0001 |  0000 0001 | 0000 0001 |
     *     |-----------|------------|------------|-----------|
     *     |    -1     |  1000 0001 |  1111 1110 | 1111 1111 |
     *     注：为了方便比较我们在这里用一个字节的整数举例。
     */

    private static void example01() {
        System.out.println("其他进制转换成十进制。");

        System.out.printf(" (10001)B = 2⁴ + 2⁰ = %s\n", (int) (Math.pow(2, 4) + Math.pow(2, 0)));

        System.out.printf("(101.01)B = 2² + 2⁰ + 2⁻² = %s\n", Math.pow(2, 2) + Math.pow(2, 0) + Math.pow(2, -2));

        System.out.printf("   (011)O = 8¹ + 8⁰ = %s\n", (int) (Math.pow(8, 1) + Math.pow(8, 0)));

        System.out.printf("    (72)O = 8¹×7 + 8⁰×2 = %s\n", (int) (Math.pow(8, 1) * 7 + Math.pow(8, 0) * 2));

        System.out.printf("  (112A)H = 16³×1 + 16²×1 + 16¹×2 + 16⁰×10 = %s\n", (int) (Math.pow(16, 3) * 1 + Math.pow(16, 2) * 1 + Math.pow(16, 1) * 2 + Math.pow(16, 0) * 10));
    }

    private static void example02() {
        System.out.println("十进制20.345转换成二进制(整数除以2直到商为零，小数乘以2直到小数为零（保留N位）)");

        System.out.println("20.345D = 10100.01011B");

        System.out.println("十进制100转成八进制、十六进制（除以8，除以16）");

        System.out.println("100D = 144F        100D = 64H");
    }

    private static void example03() {
        System.out.println("将二进制数(1101101110.110101)₂转换成八进制数。(按三位分组)");

        System.out.println("(1101101110.110101)₂ = (1556.65)₈");
    }

    private static void example04() {
        System.out.println("将二进制数(001101101110.110101)₂转换成十六进制数。(按四位分组)");

        System.out.println("(001101101110.110101)₂ = (36E.D4)₁₆");
    }

    private static void example05() {
        System.out.println("八进制、十六进制转换成二进制。");

        System.out.println("(136)₈ = (001 011 110)₂        (17A)₁₆ = (0001 0111 1010)₂");
    }

    public static void main(String[] args) {
        /* 首页/ 加密 & 解密/ 进制转换    https://www.sojson.com/hexconvert.html */
        example01();
        example02();
        example03();
        example04();
        example05();
    }

}
